package com.siyoumi.app.netty;

import com.siyoumi.app.netty.to.NettyConnectionData;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

//服务端-启动类
@Slf4j
public abstract class NettyWebSocketServer {
    @SneakyThrows
    final protected void handle(NettyConnectionData data) {
        int maxFrameSize = 1024;

        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup);
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.localAddress(new InetSocketAddress(data.getIp(), data.getPort()));
            //
            serverBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) //连接超时5秒
                    .option(ChannelOption.SO_BACKLOG, 1024) //连接队列大小
                    .option(ChannelOption.SO_KEEPALIVE,true)
                    //.option(ChannelOption.SO_BACKLOG, 2)
            ;
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    ChannelPipeline pipeline = socketChannel.pipeline();

                    //pipeline.addLast(new JdkZlibDecoder(ZlibWrapper.GZIP));
                    //pipeline.addLast(new LineBasedFrameDecoder(1024 * 1024));
                    //pipeline.addLast(new StringEncoder());
                    //pipeline.addLast(new StringDecoder());

                    //长度字段解码-粘包和拆包处理
                    //pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 2, 0, 2));
                    //pipeline.addLast(new LengthFieldPrepender(2));
                    //pipeline.addLast("encode", new StringDecoder());

                    //websocket基于http协议，所以需要http编解码器
                    pipeline.addLast(new HttpServerCodec());
                    //添加对于读写大数据流的支持
                    pipeline.addLast(new ChunkedWriteHandler());
                    //对httpMessage进行聚合
                    pipeline.addLast(new HttpObjectAggregator(65536));
                    //pipeline.addLast(new ChannelInboundHandlerAdapter() {
                    //    @Override
                    //    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                    //        if (msg instanceof FullHttpRequest) {
                    //            FullHttpRequest fullHttpRequest = (FullHttpRequest) msg;
                    //            String urlFull = fullHttpRequest.uri();
                    //            URI urlArr = URI.create(urlFull);
                    //            String url = urlArr.getPath();
                    //            String query = urlArr.getQuery();
                    //
                    //            log.debug("url:{}", url);
                    //            log.debug("query:{}", query);
                    //            if (!url.equals(data.getPath())) {
                    //                log.error("路径非法:{},{}", data.getPath(), url);
                    //                // 访问的路径不是 websocket的端点地址，响应404
                    //                NettyUtil.sendTextAndClose(ctx.channel(), "路径非法");
                    //                return;
                    //            }
                    //
                    //            InputData inputData = InputData.parse(urlFull);
                    //            String token = inputData.input("token");
                    //            if (XStr.isNullOrEmpty(token)) {
                    //                log.error("缺少token");
                    //                NettyUtil.sendTextAndClose(ctx.channel(), "缺少token");
                    //                return;
                    //            }
                    //
                    //            //检查token，签名
                    //            XReturn r = XJwt.getBean().parseToken(token);
                    //            if (r.err()) {
                    //                log.error("token：{}", r.getErrMsg());
                    //                NettyUtil.sendTextAndClose(ctx.channel(), r.getErrMsg());
                    //                return;
                    //            }
                    //
                    //            String openid = r.getData("openid");
                    //            if (XStr.isNullOrEmpty(openid)) {
                    //                log.debug("miss openid");
                    //                NettyUtil.sendTextAndClose(ctx.channel(), "miss openid");
                    //                return;
                    //            }
                    //
                    //            NettyUtil.setTokenData(ctx.channel(), r);
                    //        }
                    //
                    //        super.channelRead(ctx, msg);
                    //    }
                    //});
                    //心跳检测，实现超时断开连接
                    pipeline.addLast(new IdleStateHandler(data.getIdleTimeSeconds(), 0, 0, TimeUnit.SECONDS));
                    pipeline.addLast(new WebSocketServerCompressionHandler());
                    pipeline.addLast(new WebSocketServerProtocolHandler(data.getPath(), null, true, maxFrameSize, false,
                            true));

                    //业务处理逻辑
                    pipeline.addLast(new NettyAuthHandler());
                    //业务处理逻辑
                    pipeline.addLast(data.getHandler());
                }
            });
            Channel channel = serverBootstrap.bind().sync().channel();
            log.info("websocket 服务启动，ip={},port={},path={}", data.getIp(), data.getPort(), data.getPath());
            channel.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
